﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Reflection;
using System.ServiceModel.Channels;

namespace Ganosoft.MServiceBus.Component
{
    public class ServiceLocator
    {
        public string MServiceBusUrl { get; set; }

        private IMServiceBus serviceBus;
        private Dictionary<string, object> serviceMapByName;
        private Dictionary<string, object> serviceMapByType;
        private NetTcpBinding tcpBinding;

        public ServiceLocator()
        {
            serviceMapByName = new Dictionary<string, object>();
            serviceMapByType = new Dictionary<string, object>();

            tcpBinding = new NetTcpBinding();
            tcpBinding.PortSharingEnabled = true;
            tcpBinding.MaxReceivedMessageSize = int.MaxValue;
            tcpBinding.MaxBufferSize = int.MaxValue;
            tcpBinding.ReaderQuotas.MaxArrayLength = int.MaxValue;
            tcpBinding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
            tcpBinding.ReaderQuotas.MaxNameTableCharCount = int.MaxValue;
            tcpBinding.ReaderQuotas.MaxBytesPerRead = int.MaxValue;

            tcpBinding.Security.Mode = SecurityMode.None;
        }

        public T FindService<T>()
        {
            try
            {
                Type serviceType = typeof(T);
                string typeName = serviceType.FullName;

                if (serviceBus == null)
                    serviceBus = ChannelFactory<IMServiceBus>.CreateChannel(new NetTcpBinding(), new EndpointAddress(MServiceBusUrl));

                if (!serviceMapByType.ContainsKey(typeName))
                {
                    ServiceItem item = serviceBus.FindServiceByType(typeName);
                    if (item == null)
                    {
                        // TODO log
                        return default(T);
                    }

                    object obj = CreateService(item, serviceType);
                    serviceMapByType[typeName] = obj;
                }

                return (T)serviceMapByType[typeName];
            }
            catch
            {
                return default(T);
            }
        }

        public object FindService(string serviceName, Type serviceType)
        {
            try
            {
                if (serviceBus == null)
                    serviceBus = ChannelFactory<IMServiceBus>.CreateChannel(new NetTcpBinding(), new EndpointAddress(MServiceBusUrl));

                if (!serviceMapByName.ContainsKey(serviceName))
                {
                    ServiceItem item = serviceBus.FindServiceByName(serviceName);
                    if (item == null)
                    {
                        // TODO log.
                        return null;
                    }

                    object obj = CreateService(item, serviceType);
                    serviceMapByName[serviceName] = obj;
                }

                return serviceMapByName[serviceName];
            }
            catch
            {
                return null;
            }
        }

        private object CreateService(ServiceItem item, Type serviceType)
        {
            var channelFactoryType = typeof(ChannelFactory<>);
            channelFactoryType = channelFactoryType.MakeGenericType(serviceType);

            ChannelFactory channelFactory = (ChannelFactory)Activator.CreateInstance(channelFactoryType);
            MethodInfo createchannel = channelFactory.GetType().GetMethod("CreateChannel", new Type[] { typeof(Binding), typeof(EndpointAddress) });

            return createchannel.Invoke(channelFactory, new object[] { new NetTcpBinding(), new EndpointAddress(item.ServiceUrls[0]) });
        }
    }
}
